import time
from enum import IntEnum
from collections import defaultdict


class CanFrame:
    """CAN帧数据结构"""

    def __init__(self, can_id, data, timestamp=None):
        self.can_id = can_id
        self.data = bytearray(data)
        self.timestamp = timestamp or time.time()


class UdsReassembler:
    """UDS多帧重组器（ISO 15765-2）"""

    def __init__(self, timeout=0.1):
        self.sessions = defaultdict(dict)
        self.timeout = timeout  # 重组超时时间（秒）

    def _get_frame_type(self, frame):
        """识别帧类型"""
        pci = frame.data[0] >> 4
        if pci == 0: return 'SF'  # 单帧
        if pci == 1: return 'FF'  # 首帧
        if pci == 2: return 'CF'  # 连续帧
        if pci == 3: return 'FC'  # 流控帧
        return None

    def process_frame(self, frame):
        """处理传入的CAN帧"""
        session_key = frame.can_id
        current_session = self.sessions[session_key]
        frame_type = self._get_frame_type(frame)

        # 清理过期会话
        if 'last_time' in current_session and time.time() - current_session['last_time'] > self.timeout:
            del self.sessions[session_key]
            return None

        # 处理单帧
        if frame_type == 'SF':
            length = frame.data[0] & 0x0F
            return frame.data[1:1 + length]

        # 处理首帧
        if frame_type == 'FF':
            total_length = ((frame.data[0] & 0x0F) << 8) | frame.data[1]
            current_session.update({
                'total_length': total_length,
                'data_buffer': bytearray(total_length),
                'expected_seq': 0,
                'last_time': time.time(),
                'received_len': 0
            })
            # 保存首帧数据
            payload_len = min(6, total_length)
            current_session['data_buffer'][0:payload_len] = frame.data[2:2 + payload_len]
            current_session['received_len'] = payload_len

        # 处理连续帧
        elif frame_type == 'CF' and 'data_buffer' in current_session:
            seq_num = frame.data[0] & 0x0F
            # 检查序列号
            if seq_num != current_session['expected_seq']:
                return None

            # 添加数据到缓冲区
            max_payload = min(7, current_session['total_length'] - current_session['received_len'])
            start_idx = current_session['received_len']
            end_idx = start_idx + max_payload

            current_session['data_buffer'][start_idx:end_idx] = frame.data[1:1 + max_payload]
            current_session['received_len'] = end_idx
            current_session['expected_seq'] = (seq_num + 1) % 16
            current_session['last_time'] = time.time()

            # 检查是否完成
            if current_session['received_len'] >= current_session['total_length']:
                data = current_session['data_buffer']
                del self.sessions[session_key]
                return data

        return None


class UdsService(IntEnum):
    """UDS服务枚举"""
    DIAGNOSTIC_SESSION_CONTROL = 0x10
    READ_DTC_INFORMATION = 0x19
    ECU_RESET = 0x11
    # 其他服务...


class DtcStatus(IntEnum):
    """DTC状态位定义"""
    TEST_FAILED = 0
    FAILED_THIS_CYCLE = 1
    PENDING_DTC = 2
    CONFIRMED = 3
    WARNING_INDICATOR = 4


class DtcParser:
    """DTC故障码解析器"""

    def __init__(self, dtc_database=None):
        # DTC数据库示例：{'C12120': 'ABS通信超时', 'P0123': '节气门位置传感器A电路高'}
        self.dtc_database = dtc_database or {}

    def parse_dtc_status(self, status_byte):
        """解析DTC状态字节"""
        status = {
            'current_fault': bool(status_byte & (1 << DtcStatus.TEST_FAILED)),
            'this_cycle': bool(status_byte & (1 << DtcStatus.FAILED_THIS_CYCLE)),
            'pending': bool(status_byte & (1 << DtcStatus.PENDING_DTC)),
            'confirmed': bool(status_byte & (1 << DtcStatus.CONFIRMED)),
            'warning_lamp': bool(status_byte & (1 << DtcStatus.WARNING_INDICATOR))
        }
        return status

    def bytes_to_dtc(self, dtc_bytes):
        """将3字节转换为DTC字符串"""
        if len(dtc_bytes) != 3:
            raise ValueError("DTC必须为3字节")

        # 第一个字节分解
        byte1 = dtc_bytes[0]
        dtc_type = byte1 >> 6
        # 类型映射
        type_map = {
            0: 'P',  # 动力系统
            1: 'C',  # 底盘系统
            2: 'B',  # 车身系统
            3: 'U'  # 网络通信
        }
        prefix = type_map.get(dtc_type, 'X')
        # 后两个字节转换为4位十六进制
        code = f"{dtc_bytes[1]:02X}{dtc_bytes[2]:02X}"
        return f"{prefix}{code}"

    def parse_dtc_response(self, response_data):
        """解析0x19服务的DTC响应"""
        if not response_data or response_data[0] != 0x59:
            return None

        subfunction = response_data[1]
        dtc_list = []

        # 解析DTC数量响应 (0x19 01)
        if subfunction == 0x01 and len(response_data) >= 4:
            dtc_count = (response_data[2] << 8) | response_data[3]
            return {'type': 'count', 'count': dtc_count}

        # 解析DTC列表响应 (0x19 02)
        elif subfunction == 0x02:
            # 每4字节表示一个DTC(3字节编码+1字节状态)
            for i in range(2, len(response_data), 4):
                if i + 3 >= len(response_data):
                    break

                dtc_bytes = response_data[i:i + 3]
                status_byte = response_data[i + 3]

                dtc_code = self.bytes_to_dtc(dtc_bytes)
                status_info = self.parse_dtc_status(status_byte)
                description = self.dtc_database.get(dtc_code, "未知故障码")

                dtc_list.append({
                    'code': dtc_code,
                    'status': status_info,
                    'description': description
                })

            return {'type': 'list', 'dtcs': dtc_list}

        return None


class UdsDiagnosticTool:
    """UDS诊断主控制器"""

    def __init__(self):
        self.reassembler = UdsReassembler()
        self.dtc_parser = DtcParser()

    def set_dtc_database(self, database):
        """设置DTC数据库"""
        self.dtc_parser.dtc_database = database

    def process_can_frame(self, can_frame):
        """处理输入的CAN帧"""
        # 重组UDS消息
        uds_message = self.reassembler.process_frame(can_frame)
        if not uds_message:
            return None

        # 解析UDS响应
        service_id = uds_message[0]

        # 诊断会话响应
        if service_id == 0x50:  # 0x10 + 0x40
            session_type = uds_message[2] if len(uds_message) > 2 else None
            return {'service': 'SessionControl', 'session': session_type}

        # DTC信息响应
        elif service_id == 0x59:  # 0x19 + 0x40
            return self.dtc_parser.parse_dtc_response(uds_message)

        # 其他服务响应...
        return {'raw_data': uds_message.hex()}


# ==================== 使用示例 ====================
if __name__ == "__main__":
    # 初始化诊断工具
    tool = UdsDiagnosticTool()

    # 加载DTC数据库 (实际应用应从文件加载)
    dtc_db = {
        'C12120': 'ABS控制模块通信超时',
        'P0123': '节气门位置传感器A电路高输入',
        'B1234': '左前车门控制模块无响应'
    }
    tool.set_dtc_database(dtc_db)

    # 示例1: 单帧DTC响应
    single_frame = CanFrame(0x7E0, [0x03, 0x59, 0x02, 0xC1, 0x21, 0x20, 0xDB])
    result = tool.process_can_frame(single_frame)
    print("单帧DTC解析结果:", result)

    # 示例2: 多帧DTC响应
    # 首帧 (FF)
    frame1 = CanFrame(0x7E0, [0x10, 0x14, 0x59, 0x02, 0xC1, 0x21, 0x20, 0xDB])
    # 连续帧1 (CF)
    frame2 = CanFrame(0x7E0, [0x21, 0xB1, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD])
    # 连续帧2 (CF)
    frame3 = CanFrame(0x7E0, [0x22, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0x00])

    # 处理多帧
    tool.process_can_frame(frame1)  # 返回None (未完成重组)
    tool.process_can_frame(frame2)  # 返回None (未完成重组)
    result = tool.process_can_frame(frame3)  # 返回完整解析

    print("\n多帧DTC解析结果:")
    if result and result['type'] == 'list':
        for dtc in result['dtcs']:
            print(f"故障码: {dtc['code']} - {dtc['description']}")
            print(f"状态: 当前故障={dtc['status']['current_fault']}, 已确认={dtc['status']['confirmed']}")
            print(f"     报警灯={dtc['status']['warning_lamp']}\n")